package com.qk.management.service.serviceimpl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qk.common.PageResult;
import com.qk.common.constant.BusinessStatusConstant;
import com.qk.common.constant.ClueStatusConstant;
import com.qk.common.constant.ClueTrackRecordType;
import com.qk.common.constant.clueTrackRecordStatusConstant;
import com.qk.common.enums.CommonVariablesEnum;
import com.qk.common.enums.ParamEnum;
import com.qk.common.exception.CommonException;
import com.qk.common.util.thread.CurrentLoggedInUserIDData;
import com.qk.domain.ActivityDO;
import com.qk.domain.clue.ClueDO;
import com.qk.domain.clue.CluePoolDO;
import com.qk.dto.clue.ClueConvertToFalseClueDTO;
import com.qk.dto.clue.ClueFollowUpDTO;
import com.qk.dto.clue.ClueInsertDTO;
import com.qk.dto.clue.CluePageDTO;
import com.qk.entity.Business;
import com.qk.entity.Clue;
import com.qk.entity.ClueTrackRecord;
import com.qk.management.mapper.*;
import com.qk.management.service.ClueService;
import com.qk.vo.clue.ClueByIdVO;
import com.qk.vo.clue.ClueTrackRecordVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author 上官徵羽
 * @version 1.0
 * @since 2025-10-12 21:56
 */
@Slf4j
@Service
public class ClueServiceImpl extends ServiceImpl<ClueMapper, Clue> implements ClueService {

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private ClueTrackRecordMapper clueTrackRecordMapper;
    @Autowired
    private BusinessMapper businessMapper;
    @Autowired
    private ActivityMapper activityMapper;

    @Override
    public PageResult<ClueDO> listPage(CluePageDTO cluePageDTO) {
        Page<Clue> page = new Page<>(cluePageDTO.getPage(), cluePageDTO.getPageSize());
        LambdaQueryWrapper<Clue> clueQueryWrapper = Wrappers.<Clue>lambdaQuery()
                .eq(!ObjectUtil.isEmpty(cluePageDTO.getClueId()), Clue::getId, cluePageDTO.getClueId())
                .like(!ObjectUtil.isEmpty(cluePageDTO.getPhone()), Clue::getPhone, cluePageDTO.getPhone())
                .eq(!ObjectUtil.isEmpty(cluePageDTO.getStatus()), Clue::getStatus, cluePageDTO.getStatus())
                .eq(!ObjectUtil.isEmpty(cluePageDTO.getChannel()), Clue::getChannel, cluePageDTO.getChannel());
        Map<Integer, String> userMap = new HashMap<>();
        if (extracted(cluePageDTO, clueQueryWrapper)){
            return PageResult.<ClueDO>builder()
                    .total(page.getTotal())
                    .rows(new ArrayList<>())
                    .build();
        }
        Page<Clue> pageRust = this.baseMapper.selectPage(page, clueQueryWrapper);
        Page<ClueDO> pageResult = new Page<>();
        BeanUtils.copyProperties(pageRust, pageResult, CommonVariablesEnum.PAGE_FIELD_RECORDS.getKey());
        if (!ObjectUtil.isEmpty(pageRust.getRecords())) {
            List<Integer> userIds = pageRust.getRecords().stream()
                    .map(Clue::getUserId)
                    .filter(ObjectUtil::isNotNull)
                    .distinct()
                    .collect(Collectors.toList());
            if (!ObjectUtil.isEmpty(userIds)) {
                userMap = getUserMap(null, userIds);
            }
        }
        pageResult.setRecords(pageRust.getRecords().stream()
                .map(clue -> {
                    ClueDO clueDO = new ClueDO();
                    BeanUtil.copyProperties(clue, clueDO);
                    return clueDO;
                })
                .collect(Collectors.toList()));

        if (!ObjectUtil.isEmpty(userMap)) {
            Map<Integer, String> finalUserMap = userMap;
            pageResult.setRecords(pageResult.getRecords().stream().peek(item ->
                    item.setAssignName(finalUserMap.get(item.getUserId()))).toList());
         }
        return PageResult.<ClueDO>builder()
                .total(pageResult.getTotal())
                .rows(pageResult.getRecords())
                .build();
    }

    @Override
    public void save(ClueInsertDTO clueInsertDTO) {
        if (ObjectUtil.isEmpty(clueInsertDTO.getPhone()) || ObjectUtil.isEmpty(clueInsertDTO.getChannel())) {
            CommonException.throwCommonException(ParamEnum.PARAM_ERROR);
        }
        Clue clue = BeanUtil.copyProperties(clueInsertDTO, Clue.class);
        clue.setCreateTime(LocalDateTime.now());
        clue.setUpdateTime(LocalDateTime.now());
        clue.setStatus(ClueStatusConstant.CLUE_WAIT_ALLOT);
        this.save(clue);

    }

    @Override
    public void assignLeads(Integer clueId, Integer userId) {
        this.baseMapper.updateById(Clue.builder()
                .id(clueId)
                .userId(userId)
                .status(ClueStatusConstant.CLUE_WAIT_FOLLOW)
                .updateTime(LocalDateTime.now())
                .build());
    }

    @Override
    public ClueByIdVO getClueById(Integer id) {
        Clue clue = this.baseMapper.selectById(id);
        ClueByIdVO clueByIdVO = BeanUtil.copyProperties(clue, ClueByIdVO.class);
        List<ClueTrackRecordVO> clueTrackRecordVOList = clueTrackRecordMapper
                .selectList(Wrappers.lambdaQuery(ClueTrackRecord.class).eq(ClueTrackRecord::getClueId, id))
                .stream()
                .map(record -> {
                    ClueTrackRecordVO vo = BeanUtil.copyProperties(record, ClueTrackRecordVO.class, CommonVariablesEnum.ASSIGN_NAME.getKey());
                    vo.setAssignName(clue.getName());
                    return vo;
                })
                .toList();
        clueByIdVO.setTrackRecords(clueTrackRecordVOList);
        return clueByIdVO;
    }

    @Override
    @Transactional
    public void clueFollowUpUpdateAndInsertById(ClueFollowUpDTO clueFollowUpDTO) {
        Clue clue = BeanUtil.copyProperties(clueFollowUpDTO, Clue.class);
        clue.setUpdateTime(LocalDateTime.now());
        if (Objects.equals(clue.getStatus(), ClueStatusConstant.CLUE_WAIT_FOLLOW)) {
            clue.setStatus(ClueStatusConstant.CLUE_FOLLOWING);
        }
        ClueTrackRecord clueTrackRecord = BeanUtil.copyProperties(clueFollowUpDTO, ClueTrackRecord.class);
        clueTrackRecord.setId(null);
        clueTrackRecord.setClueId(clue.getId());
        clueTrackRecord.setUserId(clue.getUserId());
        clueTrackRecord.setCreateTime(LocalDateTime.now());
        clueTrackRecord.setType(ClueTrackRecordType.NORMALLY);
        clueTrackRecord.setUserId((CurrentLoggedInUserIDData.get()));
        this.updateById(clue);
        this.clueTrackRecordMapper.insert(clueTrackRecord);

    }

    @Override
    @Transactional
    public void clueConvertToBusiness(Integer id) {
        Clue clue = this.baseMapper.selectById(id);
        clue.setStatus(ClueStatusConstant.CLUE_CONVERT_BUSINESS);
        clue.setUpdateTime(LocalDateTime.now());
        Business business = BeanUtil.copyProperties(clue, Business.class);
        business.setId(null);
        business.setUserId(null);
        business.setStatus(BusinessStatusConstant.BUSINESS_WAIT_ALLOT);
        business.setClueId(id);
        business.setCreateTime(LocalDateTime.now());
        business.setUpdateTime(LocalDateTime.now());
        this.updateById(clue);
        this.businessMapper.insert(business);
    }

    @Override
    public void clueConvertToFalseClue(Integer id, ClueConvertToFalseClueDTO clueConvertToFalseClueDTO) {
        Clue clue = this.baseMapper.selectById(id);
        ClueTrackRecord clueTrackRecord = BeanUtil.copyProperties(clue, ClueTrackRecord.class);
        clue.setStatus(ClueStatusConstant.CLUE_FALSE);
        clue.setUpdateTime(LocalDateTime.now());
        clueTrackRecord.setUserId(clue.getUserId());
        clue.setUserId(null);
        clue.setNextTime(null);
        clueTrackRecord.setId(null);
        clueTrackRecord.setFalseReason(clueConvertToFalseClueDTO.getReason());
        clueTrackRecord.setRecord(clueConvertToFalseClueDTO.getRemark());
        clueTrackRecord.setType(clueTrackRecordStatusConstant.CONVERT_TO_FALSE_CLUE);
        clueTrackRecord.setClueId(id);
        clueTrackRecord.setNextTime(null);
        this.updateById(clue);
        this.clueTrackRecordMapper.insert(clueTrackRecord);
    }

    @Override
    public PageResult<CluePoolDO> getCluePool(CluePageDTO cluePageDTO) {
        Page<Clue> page = Page.of(cluePageDTO.getPage(), cluePageDTO.getPageSize());
        LambdaQueryWrapper<Clue> wrappers = Wrappers.<Clue>lambdaQuery()
                .eq(Clue::getStatus, ClueStatusConstant.CLUE_FALSE)
                .eq(!ObjectUtil.isEmpty(cluePageDTO.getClueId()), Clue::getId, cluePageDTO.getClueId())
                .like(!ObjectUtil.isEmpty(cluePageDTO.getPhone()), Clue::getPhone, cluePageDTO.getPhone())
                .eq(!ObjectUtil.isEmpty(cluePageDTO.getChannel()), Clue::getChannel, cluePageDTO.getChannel());
        Page<Clue> pageClue = this.baseMapper.selectPage(page, wrappers);
        Page<CluePoolDO> pageCluePoolDo = new Page<>();
        BeanUtil.copyProperties(pageClue, pageCluePoolDo, CommonVariablesEnum.PAGE_FIELD_RECORDS.getKey());
        if (!ObjectUtil.isEmpty(pageClue.getRecords())) {
            pageCluePoolDo.setRecords(pageClue.getRecords().stream()
                    .map(clue -> {
                        CluePoolDO cluePoolDo = new CluePoolDO();
                        BeanUtil.copyProperties(clue, cluePoolDo);
                        return cluePoolDo;
                    })
                    .collect(Collectors.toList()));
            List<Integer> activityIds = pageClue.getRecords().stream().map(Clue::getActivityId).distinct().toList();
            Map<Integer, String> activityMap = getActivityMap(activityIds);
            pageCluePoolDo.getRecords().forEach(cluePoolDo ->
                    cluePoolDo.setActivityName(activityMap.get(cluePoolDo.getActivityId()))
            );
        }
        return PageResult.<CluePoolDO>builder()
                .total(pageCluePoolDo.getTotal())
                .rows(pageCluePoolDo.getRecords())
                .build();
    }

    private Map<Integer, String> getActivityMap(List<Integer> activityIds) {
        return activityMapper.selectMaps(Wrappers.<ActivityDO>lambdaQuery()
                        .select(ActivityDO::getId, ActivityDO::getName)
                        .in(ActivityDO::getId, activityIds))
                .stream().collect(Collectors.toMap(
                        map -> Integer.parseInt(map.get(CommonVariablesEnum.ID.getKey()).toString()),
                        map -> map.get(CommonVariablesEnum.NAME.getKey()).toString()));
    }

    private Map<Integer, String> getUserMap(String name, List<Integer> ids) {
        List<Map<String, Object>> userList = userMapper.getMap(name, ids);
        return userList.stream().collect(
                Collectors.toMap(
                        map -> Integer.parseInt(map.get(CommonVariablesEnum.ID.getKey()).toString()),
                        map -> map.get(CommonVariablesEnum.NAME.getKey()).toString()));
    }

    private boolean extracted(CluePageDTO cluePageDTO, LambdaQueryWrapper<Clue> clueQueryWrapper){
        if (!ObjectUtil.isEmpty(cluePageDTO.getAssignName())) {
            Map<Integer,String> userMap = getUserMap(cluePageDTO.getAssignName(), null);
            if (ObjectUtil.isEmpty(userMap)) {
                return true;
            }
            clueQueryWrapper.in(Clue::getUserId, userMap.keySet());
        }
        return false;
    }
}
